package cn.gpnusz.oliverwiki.service;

import cn.gpnusz.oliverwiki.entity.Content;
import cn.gpnusz.oliverwiki.entity.ContentExample;
import cn.gpnusz.oliverwiki.entity.Doc;
import cn.gpnusz.oliverwiki.entity.DocExample;
import cn.gpnusz.oliverwiki.exception.BusinessException;
import cn.gpnusz.oliverwiki.exception.BusinessExceptionCode;
import cn.gpnusz.oliverwiki.mapper.ContentMapper;
import cn.gpnusz.oliverwiki.mapper.DocMapper;
import cn.gpnusz.oliverwiki.mapper.MyDocMapper;
import cn.gpnusz.oliverwiki.req.DocSaveReq;
import cn.gpnusz.oliverwiki.resp.DocQueryResp;
import cn.gpnusz.oliverwiki.util.RedisUtil;
import cn.gpnusz.oliverwiki.util.RequestContext;
import cn.gpnusz.oliverwiki.util.SnowFlake;
import cn.gpnusz.oliverwiki.websocket.WebSocketServer;
import org.slf4j.MDC;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author h0ss
 * @description 电子书文档业务层
 * @date 2021/9/6 13:17
 */
@Service
public class DocService {
    @Resource
    private DocMapper docMapper;

    @Resource
    private MyDocMapper myDocMapper;

    @Resource
    private ContentMapper contentMapper;

    @Resource
    private SnowFlake snowFlake;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private WsService wsService;


    /**
     * 根据电子书id查询其所有文档数据的业务方法
     *
     * @param ebookId : 电子书id
     * @return : java.util.List<cn.gpnusz.oliverwiki.resp.DocQueryResp>
     * @author h0ss
     */
    public List<DocQueryResp> getAll(Long ebookId) {
        DocExample docExample = new DocExample();
        docExample.setOrderByClause("sort asc");
        DocExample.Criteria criteria = docExample.createCriteria();
        criteria.andEbookIdEqualTo(ebookId);
        List<Doc> docList = docMapper.selectByExample(docExample);
        return getDocResp(docList);
    }

    /**
     * 新增或编辑电子书文档的业务方法
     *
     * @param docSaveReq : 保存的电子书文档数据
     * @author h0ss
     */
    @Transactional(rollbackFor = Exception.class)
    public void save(DocSaveReq docSaveReq) {
        Doc doc = new Doc();
        BeanUtils.copyProperties(docSaveReq, doc);
        Content content = new Content();
        BeanUtils.copyProperties(docSaveReq, content);
        // 判断是新增还是编辑
        if (doc.getId() != null) {
            DocExample docExample = new DocExample();
            DocExample.Criteria criteria = docExample.createCriteria();
            criteria.andIdEqualTo(doc.getId());
            docMapper.updateByExample(doc, docExample);
            ContentExample contentExample = new ContentExample();
            ContentExample.Criteria criteriaContent = contentExample.createCriteria();
            criteriaContent.andIdEqualTo(doc.getId());
            int count = contentMapper.updateByExampleWithBLOBs(content, contentExample);
            if (count == 0) {
                contentMapper.insert(content);
            }
        } else {
            doc.setId(snowFlake.nextId());
            // 将点赞数阅读数置0 否则会存入null
            doc.setViewCount(0);
            doc.setVoteCount(0);
            content.setId(doc.getId());
            docMapper.insert(doc);
            contentMapper.insert(content);
        }
    }

    /**
     * 删除电子书文档的业务方法
     *
     * @param id : 要删除的电子书文档id
     * @author h0ss
     */
    public void delete(Long id) {
        DocExample docExample = new DocExample();
        DocExample.Criteria criteria = docExample.createCriteria();
        criteria.andIdEqualTo(id);
        docMapper.deleteByExample(docExample);
    }

    /**
     * 按id列表删除电子书文档的业务方法
     *
     * @param ids : 要删除的电子书文档id列表
     * @author h0ss
     */
    public void delete(String ids) {
        DocExample docExample = new DocExample();
        DocExample.Criteria criteria = docExample.createCriteria();
        if (ids != null) {
            List<String> condition = Arrays.asList(ids.split(","));
            criteria.andIdIn(condition);
        }
        docMapper.deleteByExample(docExample);
    }

    /**
     * 根据传入的id查询文档内容
     *
     * @param id : 文档id
     * @return : java.lang.String
     * @author h0ss
     */
    public String findContent(Long id) {
        // 先将该文档的阅读数+1
        myDocMapper.increaseViewCount(id);
        // 文档内容查询
        ContentExample contentExample = new ContentExample();
        ContentExample.Criteria criteria = contentExample.createCriteria();
        criteria.andIdEqualTo(id);
        List<Content> contents = contentMapper.selectByExampleWithBLOBs(contentExample);
        if (contents.size() > 0) {
            Content content = contents.get(0);
            if (content != null) {
                return content.getContent();
            }
        }
        return "";
    }

    /**
     * 增加文档点赞数的业务方法
     * 增加文档点赞后的消息通知功能
     *
     * @param id : 文档id
     * @author h0ss
     */
    public void increaseVoteCount(Long id) {
        // IP+文章id的组合作为key存入redis中，可保证3day内不能重复点赞
        final String key = "DOC_VOTE_" + id + "_" + RequestContext.getRemoteAddr();
        final long expireTime = 3600 * 24 * 3;
        if (redisUtil.validateRepeat(key, expireTime)) {
            myDocMapper.increaseVoteCount(id);
        } else {
            throw new BusinessException(BusinessExceptionCode.VOTE_REPEAT);
        }
        // 点赞消息通知
        noticeVoteInfo(id);
    }

    /**
     * 点赞消息通知功能
     *
     * @param id : 文章id
     * @author h0ss
     */
    public void noticeVoteInfo(Long id) {
        DocExample example = new DocExample();
        DocExample.Criteria criteria = example.createCriteria();
        criteria.andIdEqualTo(id);
        List<Doc> docs = docMapper.selectByExample(example);
        String name = docs.get(0).getName();
        String msg = "文章【" + name + "】刚刚被点赞了！";
        // 获取日志流水号 传递到消息通知日志中
        String logId = MDC.get("LOG_ID");
        wsService.sendInfo(msg, logId);
    }

    /**
     * 将电子书文档查询结果封装成DocQueryResp集合的业务方法
     *
     * @param docList :
     * @return : java.util.List<cn.gpnusz.oliverwiki.resp.DocQueryResp>
     * @author h0ss
     */
    private List<DocQueryResp> getDocResp(List<Doc> docList) {
        List<DocQueryResp> respList = new ArrayList<>();
        for (Doc doc : docList) {
            DocQueryResp docQueryResp = new DocQueryResp();
            BeanUtils.copyProperties(doc, docQueryResp);
            respList.add(docQueryResp);
        }
        return respList;
    }
}
